#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<unordered_map> 
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
char* g_argv[128];
int g_argc=0;
char* pwd=getenv("PWD");
char* oldpwd=getenv("OLDPWD");
unordered_map<string,string> alias_list;
//先设定几个状态
#define NONE 0
#define INPUT 1
#define OUTPUT 2
#define APPEND 3
int mode=0;
//1,输入重定向:<,将文件的内容作为命令输入，cat < file.txt
//2,输出重定向:>,保存命令的输出到文件之中,echo "hhh" > file.txt
//3,追加输出重定向:>>,以追加输入的方式将命令的输出追加到文件的末尾,echo "append" >> file.txt
string filename;
void cmdprint()
{
	printf("[%s@hcss-ecs-991f:%s]# ",getenv("USER"),pwd);
}
void cmdget(char* cmdline,int size)
{
	//char* fgets("容器地址","容器大小","从哪里获取")
	char* ret=fgets(cmdline,size,stdin);
	if(ret==NULL)
	{
		printf("输入失败\n");
		exit(1);
	}
	//处理最后一个字符'\n'，将其变为'\0'
	cmdline[strlen(cmdline)-1]=0;
//	printf("%s",cmdline);
}
void cmdtoken(char* cmdline)
{
	//#include<string.h> char* strtok(char* str,const char* delim)，其中delime表示的是用于标识分隔位置的字符串
	string text=cmdline;
//	printf("%d\n",text.find("alias"));
	if(text.find("alias")==0)
	{
		//执行alias的字符串分割逻辑
		g_argc=0;
		g_argv[g_argc++]=strtok(cmdline," ");
		g_argv[g_argc++]=strtok(NULL,"\0");
		g_argv[g_argc]=NULL;
//		int i=0;
//		for(;g_argv[i];i++ )
//		{
//			printf("%s ",g_argv[i]);
//		}
//		printf("\n");
	}
	else
	{

	g_argc=0;
	g_argv[g_argc++]=strtok(cmdline," ");
	while(g_argv[g_argc-1]!=NULL)
	{
		g_argv[g_argc++]=strtok(NULL," ");
	}
	//for(int i = 0; g_argv[i] != NULL; i++) {
	//	printf("%s\n",g_argv[i]);
	//}

//	int j=0;
	g_argv[g_argc] = NULL;
	g_argc--;
	//for(;j<g_argc+1;j++)
	//{
	//	printf("%s ",g_argv[j]);
	//}
	//printf("%d\n",g_argc);
	}
}
void cmdexecute()
{
	//使用execvp函数,并且创建一个子进程来执行命令
	pid_t rid=fork();
	if(rid==0)
	{
		//子进程
//		printf("%s\n",*g_argv);
//		char* way;
//		getcwd(way,100);
//		printf("%s\n",way);
//		printf("%s  %s\n",g_argv[0],*g_argv);
		if(mode==INPUT)
		{
			int fd=open(filename.c_str(),O_RDONLY);
			if(fd<0)
			{
				exit(0);
			}
			dup2(fd,0);
			close(fd);
		
		}
		else if(mode==OUTPUT)//ls -l>log.txt
		{
			int fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_TRUNC,0666);
			if(fd<0)
			{
				exit(0);
			}
			dup2(fd,1);
			close(fd);
		}
		else if(mode ==APPEND)
		{
			int fd=open(filename.c_str(),O_CREAT|O_WRONLY|O_APPEND,0666);
			if(fd<0)
			{
				exit(0);
			}
			dup2(fd,1);
			close(fd);
		}
		int ret=execvp(g_argv[0],g_argv);
		if(ret==-1)
		{
			printf("该命令错误\n");
			perror("命令执行失败!");
		}
	}
	pid_t id=waitpid(rid,NULL,0);
	(void)id;
}
bool checkbuiltin(char** g_argv)
{
	//printf("%s",g_argv[0]);
	//内建命令：cd,echo,export,alias
	if(strcmp(g_argv[0],"cd")==0
			||strcmp(g_argv[0],"echo")==0
			||strcmp(g_argv[0],"export")==0
			||strcmp(g_argv[0],"alias")==0)
	{
		return true;
	}
	return false;
}
void my_cd()
{
	//cd命令是为了转换当前的工作路径
	//当只有一个cd时，就是切换到当前用户的家目录下
	if(g_argc==1)
	{
		getcwd(oldpwd,100);
		const char* home=getenv("HOME");
		//#include<unistd.h>  int chdir(const char* path);
		int ret=chdir(home);
		if(ret==-1)
		{
			printf("该用户不存在家路径\n");
		}
		
	}
	else
	{
		//printf("%s\n",g_argv[1]);
		//当第二个参数为~,则是切换到该用户的家目录
		if(strcmp(g_argv[1],"~")==0)
		{
			getcwd(oldpwd,100);
			const char* home=getenv("HOME");
			//#include<unistd.h>  int chdir(const char* path);
			int ret=chdir(home);
			if(ret==-1)
			{
				printf("该用户不存在家路径\n");
				return;
			}
		}
		//返回上一次的工作路径
		else if(strcmp(g_argv[1],"-")==0)
		{
			if(oldpwd==NULL)
			{
				printf("上一次工作路径为空!\n");
				return;
			}
			else
			{
				char cwd[100];
//				printf("oldpwd:%s\n",oldpwd);
				getcwd(cwd,100);
				int ret=chdir(oldpwd);
				if(ret==-1)
				{
					printf("路径转换失败\n");
					return;
				}
				strcpy(oldpwd,cwd);
//				printf("new oldpwd:%s\n",oldpwd);
			}
		}
		else
		{
			getcwd(oldpwd,100);
			const char* cwd=g_argv[1];
			//printf("cwd: %s\n", cwd);
			int ret=chdir(cwd);
			if(ret==-1)
			{
				printf("路径转换失败\n");
				return;
			}
		
			//printf("%s\n",oldpwd);
		}
	}
	getcwd(pwd,100);
}
int lastcode=0;
void my_echo()
{
	//echo我们在这里分成三种情况：1,echo "helloworld"
	//							  2,echo $?
	//							  3,echo $PATH
	if(g_argc==1)
	{
		printf("echo参数过少\n");
	}
	else
	{
		string p=g_argv[1]; 
		if(p=="$?")
		{
			//打印最近一次进程的退出码
			printf("%d\n",lastcode);
			lastcode=0;
		}
		else if(p[0]=='$')
		{
			//打印环境变量里边具体的内容
			const char* content=g_argv[1]+1;
			const char* get=getenv(content);
			printf("%s\n",get);
		}
		else
		{
			printf("%s\n",g_argv[1]);
		}
	}
}
void my_export()
{
	//exprot:一个用于将变量导出为环境变量或者更改环境变量的命令
	//#include<stdlib.h>   int setenv(const char* name,const char* value,int overwrite); 
	//overwrite:0表示不覆盖已有变量,非零则表示覆盖已有变量
	if(g_argc==1)
	{
		printf("export参数过少\n");
	}
	else 
	{
		if(g_argc==3)
		{
			//覆盖已有变量
			setenv(g_argv[1],g_argv[2],1);
		}
		else
		{
			//增加环境变量PATH的内容
			strtok(g_argv[1],"H");
			char* get=strtok(NULL,"\0");
			if(get==NULL)
			{

				printf("export命令调用错误!\n");
			}
			else
			{

				string path=getenv("PATH");
				path+=get;
				printf("%s\n",path.c_str());
				setenv("PATH",path.c_str(),1);
			}
		}
	}
}
void my_alias()
{
	//alias命令的作用是给命令取别名
	//示例:alias ll="ls -a -l"
	if(g_argc==1)
	{
		printf("alias命令调用错误!\n");
	}
	else
	{
	//	printf("g_argv[1]:%s\n",g_argv[1]);
		char* nick_name=strtok(g_argv[1],"=");
//		printf("order_name:%s\n",order_name);
//		char* next=strtok(NULL,"\0");
//		printf("下一次将从%s开始分割\n",next);
		char* order_name=strtok(NULL,"\"");
//		printf("nick_name:%s\n",nick_name);
		string order=order_name;
		string nick=nick_name;
	//	printf("order:%s\nnick:%s\n",order.c_str(),nick.c_str());
		alias_list.insert(make_pair(nick_name,order_name));
//		printf("nick:%s order:%s\n",nick.c_str(),alias_list[nick].c_str());
	}
}
void killspace(char* cmd,int& end)
{
	//该函数的作用是为了得到后面的文件名字
	while(isspace(cmd[end]))
	{
		end++;
	}
}
void redirect(char* cmd)
{
	//首先我们应该判断这是一个什么重定向
	int start=0;
	int end=strlen(cmd)-1;//最后一个字符的下标
	while(end>start)
	{
		if(cmd[end]=='<')//输入重定向
		{
			//例子:"cat < log.txt"
			cmd[end++]=0;
			//去掉空格
			killspace(cmd,end);
			filename=cmd+end;
			mode=INPUT;
			break;
		}
		else if(cmd[end]=='>')
		{
			if(cmd[end-1]=='>')//此时为追加输出重定向
			{
				//例子:"ls -l -a >>log.txt"
				cmd[end-1]=0;
				cmd[end++]=0;
				killspace(cmd,end);
				filename=cmd+end;
				mode=APPEND;
				break;

			}
			else//此时为普通输出重定向
			{
				//例子:" ls -l -a>log.txt"	
				cmd[end++]=0;
				killspace(cmd,end);
				filename=cmd+end;
				mode=OUTPUT;
				break;
			}
		}
		else
		{
			end--;
		}
	}
}
int main()
{
	while(1)
	{
		mode=NONE;
		//1,打印命令行提示符
		cmdprint();
		char cmdline[100];
		//2,输入命令并且将其放入字符数组中
		cmdget(cmdline,sizeof(cmdline));
		//判断该命令是否被alias重命名
		if(alias_list.find(cmdline)!=alias_list.end())
		{
			const char* order=alias_list[cmdline].c_str();
			strcpy(cmdline,order);
		}
		//3,判断重定向
		redirect(cmdline);
		cmdtoken(cmdline);
		//判断是否为内建命令
		if(checkbuiltin(g_argv))
		{
			if(strcmp(*g_argv,"cd")==0)
			{
				my_cd();
				continue;
			}
			else if(strcmp(*g_argv,"echo")==0)
			{
				my_echo();
				continue;
			}
			else if(strcmp(*g_argv,"export")==0)
			{
				my_export();
				continue;
			}
			else if(strcmp(*g_argv,"alias")==0)
			{	
				my_alias();
				continue;
			}
		}
		//4,执行用户输入的命令
		cmdexecute();
	}
	return 0;
}
